home *** CD-ROM | disk | FTP | other *** search
- /*
- FILE: Shutter.c
-
- Main C Source Code
-
- This entire file is Copyright ©1996 Ross Younger. You are permitted to use portions
- in your own work, as long as you give me a mention in your finished product.
- */
-
- #include "Shutter.h"
-
- /*
- I could quite happily have put the constants and prototypes into Shutter.h; if they needed to be accessed by multiple source
- files, I could have explicitly included the header into the project. Alternatively I could have put the entire contents of
- Shutter.h here. But that would have made this bit of the source too long and untidy in my opinion. So in the end, it is a
- compromise between avoiding the ultimate one-source file and keeping it all together in the same file where you can see it.
- */
-
-
- /* CONSTANTS */
- #define myResType 'Pref'
- #define myPrefsID 64
- #define dMain 128
- #define dPrefs 129
- #define kErrorStrID 256
- #define aProblems 512
- #define kMinSpace 8192 /* The amount of free space your application requires in order to run smoothly, as measured
- immediately after Setup. The only way to find out what's right is to torture test it yourself!
- I tend to round up to the nearest 4K or so. */
-
- /* GLOBALS */
- PrefsType gPrefs;
-
- /* PROTOTYPES (only the required ones) */
- void AlertUser(short index);
- void SetBoolControl(DialogPtr dlog, short item, SInt16 value);
- void doPrefs (void);
- Boolean Sys7Check (void);
- Boolean TrapAvailable(short theTrap);
- TrapType GetTrapType (short theTrap);
- short NumToolboxTraps (void);
- void DrawDefaultButton(DialogPtr thisDlog, short thisItem);
- void HiliteAControl (DialogPtr theDlog, short theItem, short theValue);
- void Flash (DialogPtr theDlog, short theItem);
- /**********************************************************************************/
- // M A I N F U N C T I O N S
- /**********************************************************************************/
- void Setup(void)
- {
- long reply, reply2;
- for (reply=0; reply<4; reply++)
- MoreMasters(); // Allocate 4 handle blocks for us to use.
- // Unpack the Mac Toolbox now, but only what we need (to save memory).
- InitGraf(&qd.thePort);
- InitResources();
- InitWindows();
- InitDialogs(0L);
- InitFonts();
- // Check for sys 7
- if (!Sys7Check)
- AlertUser(2); // Call our abandon ship routine, telling it which string to use in the dialog
- /*
- Call Gestalt for CPU type. We want >= MC68020. Check out "Gestalt.h" for details.
- (Although 020 is not ESSENTIAL here, I check for it anyway, as an example of using Gestalt.)
- */
- Gestalt('cput', &reply);
- if (reply < 3)
- AlertUser(3);
- // Check the app's memory allocation
- PurgeSpace(&reply, &reply2); // returns total mem, then max contiguous mem
- if (reply < kMinSpace)
- {
- if (UnloadScrap() != noErr) // If not enough, try unloading the scrap...
- AlertUser(1); // (exiting stage right if there is a problem with that)
- else {
- PurgeSpace(&reply, &reply2); //... and check again.
- if (reply < kMinSpace)
- AlertUser(1);
- }
- }
- FlushEvents(everyEvent, 0);
- }
- /**********************************************************************************/
- void GetPrefsFromResource(void) // Gets the prefs from the resource (within the app) & copies to global (structured) variable.
- {
- Handle myRes=GetResource(myResType, myPrefsID);
- HLock(myRes); /* Lock the handle so it can't move about and become discontiguous,
- which would cause all sorts of nasty gremlins when we do the BlockMove! */
- BlockMove(*myRes, &gPrefs, sizeof(gPrefs));
- // BlockMove takes WhereFrom (as a Ptr), WhereTo (a Ptr again), Size in bytes.
- HUnlock(myRes); // Don't leave the handle locked!
- DetachResource(myRes); // This makes the handle "forget" that it's a particular resource in the app
- DisposeHandle(myRes); // … and this deallocates the handle completely.
-
- }
- /*
- If you are accessing a file that is not the application itself, you will need to mess around with FSpOpenResFile, CloseResFile,
- etc, and perhaps the Standard File package. There's probably some code out there which will show you how, but not in this
- project. The caveat from using resources within the app is that you cannot write to them on a locked disk.
-
- Ideally all applications should NEVER edit themselves. This should be a major target of yours. Use a prefs file instead.
- Despite the fact that this one doesn't. :-)
-
- */
- /**********************************************************************************/
- void SavePrefsIntoResource(void) // Saves the prefs back into the prefs resource.
- {
- Handle myRes=GetResource(myResType, myPrefsID);
- HLock(myRes); /* Lock the handle so it can't move about and become discontiguous,
- which would cause all sorts of nasty gremlins when we do the BlockMove! */
-
- BlockMove(&gPrefs, *myRes, sizeof(gPrefs));
- // Notice the striking similarity between the above line and one in the GetPrefsFromResource function!
-
- HUnlock(myRes); // Don't leave the handle locked!
-
- ChangedResource(myRes); // Mark the resource as changed
- WriteResource(myRes); // Save the data
- if (ResError())
- {
- SysBeep(0);
- DisposeHandle(myRes); // If we get a resource error (probably to say that the app is locked or is on a locked disk)
- return; // then deallocate the memory used by the handle, and forget about it quietly. In your program you might
- // want to put up an error message (via AlertUser or something similar) at this point.
- }
- DetachResource(myRes); // Remove the handle from its file
- DisposeHandle(myRes); // … and deallocate it.
- UpdateResFile(CurResFile()); // Save the change.
- }
- /* * * DANGER * * *
- This method of accessing resources is dangerous, in that if the program quits unexpectedly (or crashes) it may not be able to
- complete the update of the resource (due to the disk cache), and that may damage the application. Always keep a backup.
- This is another reason why you should use a prefs file!
- */
- /**********************************************************************************/
- pascal Boolean DialogFilterProc (DialogRef theDialog, EventRecord *theEvt, short *itemHit)
- {
- Boolean filtered=false;
- const short eventKind = (*theEvt).what;
- const char theKey = (char)((*theEvt).message & charCodeMask);
- // The above line converts ALL keypresses to single char ASCII, even modified ones such as Command-keystrokes.
- // To check for Command, etc, use an expression like (theEvt.modifiers && cmdKey).
-
- if (eventKind == keyDown) // If we get a key depressed message, activate the hilite of the item.
- { // Note that we are NOT interested in autoKey's here, but *you* might be.
- filtered=true;
- switch (theKey)
- {
- case 'Y': case 'y': // Y for Yes
- case 13: case 3: // [or Return, or Enter... Enter returns ASCII 3, btw]
- Flash (theDialog, 1);
- *itemHit=1;
- break;
- case 'N': case 'n': // N for No(vember)
- Flash (theDialog, 2);
- *itemHit=2;
- break;
- case 'S': case 's': // S for Shutdown
- Flash (theDialog, 4);
- *itemHit=4;
- break;
- case 'R': case 'r': // R for Restart
- Flash (theDialog, 5);
- *itemHit=5;
- break;
- default: // other, unsupported key
- filtered=false;
- break;
- }
- }
- /*
- I suppose I *could* look for H for Help, but I don't know if that's all that
- much use from the keyboard, since it only does an HMSetBalloons(). And
- balloons depend on the mouse position anyway.
- */
-
- else if (eventKind == updateEvt) // If we're told to update
- DrawDefaultButton (theDialog, 1); // ...redraw this round rect that we added ourselves.
- // (the ModalDialog trap takes care of redrawing the dialog items)
- // BTW, we don't return true for this event since the system dialog handler might want it.
-
- return (filtered); // If true, the event is not processed any further.
- }
-
- /**********************************************************************************/
- void RunDialog (void) // Runs the main dialog
- {
- Boolean shutDown = (gPrefs.myPrefsShort & 0x0001); // Logical 'AND'. This takes 0x0001 as Shutdown default,
- // and 0x0000 as Restart default.
- Boolean exit=false;
- Boolean cancelled=false;
- short theItem=0;
- Boolean origBalloons=HMGetBalloons(); // Get (& store) current
- Boolean curBalloons=origBalloons; // Balloon Help state
-
- CGrafPtr saveP; // So we can restore the original GWorld before exiting this fn.
- GDHandle saveD; // (Not necessary, since there is no window underneath, but good manners anyway.)
-
- DialogRef theDlog = GetNewDialog (dMain, 0L, (WindowPtr) -1); // Dialog ID, Dialog storage, Behind What (-1 = nothing)
-
- GetGWorld(&saveP, &saveD); // Store current GWorld (see above)
- SetGWorld((CGrafPtr)theDlog, GetMainDevice()); // Set current GWorld to be the dlog
-
- if (theDlog==0L) // if no dialog resource or not enough memory, then…
- {
- SysBeep(1); // Warn user…
- ExitToShell(); // and bail out. This shouldn't happen after the program has been developed.
- }
-
- // We could use a DrawDialog(theDlog) to draw the dialog's items, but this is taken care of, at least the first time.
- DrawDefaultButton(theDlog, 1); // Draw its default button ourselves
-
- SetBoolControl(theDlog, 4, shutDown); // Hilite the default option
- SetBoolControl(theDlog, 5, !shutDown); // …and not the other.
- do {
- ModalDialog(DialogFilterProc, &theItem); /* Run the dialog, and return the item clicked.
- (NB. Only enabled items [thru ResEdit] are returned) */
- switch (theItem)
- {
- case 1: // Yes button
- exit=true;
- cancelled=false;
- break;
- case 2: // No button
- exit=true;
- cancelled=true;
- break;
- case 3: // Help button (toggles Balloon Help)
- curBalloons=HMGetBalloons();
- curBalloons=!curBalloons;
- HMSetBalloons(curBalloons);
- break;
- case 4: // Shut Down [radio]
- shutDown=true;
- SetBoolControl(theDlog, 4, shutDown);
- SetBoolControl(theDlog, 5, !shutDown);
- break;
- case 5: // Restart [radio]
- shutDown=false;
- SetBoolControl(theDlog, 4, shutDown);
- SetBoolControl(theDlog, 5, !shutDown);
- break;
- case 7: // Logo [which triggers an easter egg]
- doPrefs();
- break;
- }
- } while (!exit);
-
- HMSetBalloons(origBalloons); // Restore Balloon Help's original state.
-
- SetGWorld(saveP, saveD); // Restore original GWorld
- DisposeDialog(theDlog); // Get rid of the dlog to free up the memory we used
-
- // Now react to the user's choice.
- if (!cancelled) // If not cancelled…
- if (shutDown)
- ShutDwnPower(); // Shut down the Mac, or
- else ShutDwnStart(); // Restart, as applicable.
- }
- /**********************************************************************************/
- pascal Boolean prefsDialogFilter (DialogRef theDialog, EventRecord *theEvt, short *itemHit)
- {
- if ((*theEvt).what == updateEvt)
- DrawDefaultButton(theDialog, 1); // This is the only thing we need to update.
- // We do not return true from this so the system can update the rest.
- return (false);
- /* There _could_ be another keypress catching routine here. */
- }
- /**********************************************************************************/
- void doPrefs (void) // Runs the Easter Egg & prefs dialog
- {
- Boolean exit=false;
- Boolean myPref = (gPrefs.myPrefsShort && 0x0001); // Logical AND again. 0x0001 (or true) for Shut Down.
- short theItem=0;
- CGrafPtr saveP; // These two ARE needed this time because we want to go back
- GDHandle saveD; // to the dialog underneath when we're finished.
- DialogRef myDialog = GetNewDialog (dPrefs, 0L, (WindowPtr) -1);
-
- GetGWorld(&saveP, &saveD); // Store current GWorld
- SetGWorld((CGrafPtr)myDialog, GetMainDevice()); // Set current GWorld to be the dlog
-
- DrawDialog (myDialog);
-
- if (myDialog==0L) // if no dialog resource or not enough memory, then…
- {
- SysBeep(1); // Warn user…
- ExitToShell(); // and bail out. This shouldn't happen after the program has been developed.
- }
- SetBoolControl(myDialog, 3, myPref); // Set current pref
- SetBoolControl(myDialog, 4, !myPref); // and not the other.
-
-
- do {
- ModalDialog (prefsDialogFilter, &theItem);
- switch (theItem)
- {
- case 1: // OK
- exit=true;
- break;
-
- case 3: // Shut Down button
- myPref=true;
- SetBoolControl(myDialog, 3, true);
- SetBoolControl(myDialog, 4, false);
- break;
-
- case 4: // Restart button
- myPref=false;
- SetBoolControl(myDialog, 3, false);
- SetBoolControl(myDialog, 4, true);
- break;
- }
-
- } while (!exit);
-
- SetGWorld(saveP, saveD); // Restore original GWorld
- DisposeDialog(myDialog); // Get rid of the dlog to free up the memory we used
-
- if (myPref)
- gPrefs.myPrefsShort = 0x0001;
- else gPrefs.myPrefsShort = 0x0000;
-
- // gPrefs.myPrefsShort = ( (myPref) ? 0x0001 : 0x0000 );
- /*
- The above line probably appears to be double-Dutch [or treble-COBOL :-) ] at first. But fret not. All it does is set up my
- global prefs variable with the new state of the default.
-
- The line is an example of an inline, and it is equivalent to:
-
- {
- if (myPref)
- gPrefs.myPrefsShort = 0x0001;
- else gPrefs.myPrefsShort = 0x0000;
- }
-
- See? Generally, the syntax is ( (condition) ? valueIfTrue : valueIfFalse ). The values _can_ be snippets of code, but this
- may slow things down.
-
- Looks aside, the only difference is that an inline is compiled into an awful lot less code, and is a lot faster.
- NB. Some compilers do not allow inlines, in which case you will need to replace the inline line with the commented
- block of code. Some compilers will accept inlines but not compile them as such. Some (eg MetroWerks) have an
- option to turn inlining on or off.
-
- */
-
- SavePrefsIntoResource(); // This does exactly what it says!
- }
- /**********************************************************************************/
- void main(void)
- {
- MaxApplZone();
- // We could check the option key or command key here to show prefs dialog. But I'm just accessing it from the main dialog.
- // It would be something to do with the OAPP AppleEvent and (eventRecord.modifiers && cmdKey), I think.
- Setup();
- GetPrefsFromResource();
- RunDialog();
- /*
- I usually put a CleanUp() routine in, which takes down any special handlers, or
- anything else which needs to be taken down, but there's nothing to clean up here.
- */
-
- // The end! The prog quits automatically.
- }
- //*****************************************************************************************
- // D I A L O G U T I L I T I E S
- //*****************************************************************************************
- void AlertUser(short index) // This fn is the ULTIMATE bail-out for SERIOUS errors.
- {
- Str255 str1;
- short dummy;
- GetIndString(str1, kErrorStrID, index); // Taken out of the STR# resource with ID index. No string and we're dead!
- ParamText(str1, "\p", "\p", "\p"); // Set it up as parameter ^0, with parms ^1, ^2 and ^3 as empty [pascal] strings.
- dummy=Alert(aProblems, 0); // Run the standard alert. It gives a return value of the item hit,
- // but we ignore this as there's only one.
- ExitToShell(); // Scotty, get us out of here!
- }
- //*****************************************************************************************
- void SetBoolControl(DialogPtr dlog, short item, short value) // Sets the value of a boolean control item (radio button, check box)
- {
- Handle itemHandle;
- short itemType;
- Rect itemRect;
-
- GetDialogItem(dlog, item, &itemType, &itemHandle, &itemRect); // Grab a handle to it, etc
- if ((itemType != statText) && (itemType != editText)) // disallow text items
- SetCtlValue((ControlHandle)itemHandle, value); // Change the value now. (1=on, 0=off)
- else SysBeep(1); /* Warn, if the item is of an incorrect type
- This line should NEVER need to be called in the finished program! */
- }
- //*****************************************************************************************
- void HiliteAControl (DialogPtr theDlog, short theItem, short theValue)
- {
- Handle itemHandle;
- short itemType;
- Rect rect;
-
- GetDialogItem(theDlog, theItem, &itemType, &itemHandle, &rect); // Get item's handle, etc, etc
-
- if ((itemType != statText) && (itemType != editText)) // disallow text items
- HiliteControl( (ControlRef) itemHandle, theValue); // Hilite as appropriate (1=on, 0=off)
- else SysBeep(1); /* Need a warning if its not the right item type. (Only control items can be hilited.)
- A FINISHED PROGRAM SHOULD NEVER SEE THIS LINE EXECUTED! */
- }
- //***************************************************************************************************
- Rect GetItemsBox(DialogRef theDialog, short itemNo) // Gets the surrounding Rect of a dialog item
- {
- short itemType=0;
- Handle item=0L;
- Rect Box;
- GetDialogItem(theDialog, itemNo, &itemType, &item, &Box);
- return(Box);
- }
- //***************************************************************************************************
- void DrawDefaultButton(DialogPtr thisDlog, short thisItem)
- { // Draws that nice round Rect around a dialog's default item
- Rect myRect;
- RGBColor theColour;
-
- theColour.red=theColour.green=theColour.blue=0L; // Black is 0/65535 red, same green, same blue.
- RGBForeColor(&theColour); // Set the fg colour to black.
-
- myRect = GetItemsBox (thisDlog, thisItem);
- InsetRect(&myRect, (short)-4, (short)-4); // Push the rect out 4 pixels either way to clear the button
- PenSize(3, 3); // THICK item rect.
- FrameRoundRect(&myRect, 15, 15); // Draw it.
- /*
- This assumes that thisDlog is set as the current GWorld. If its not then that causes probs.
- (We _could_ program our way round this if necessary.)
- */
- }
- //*****************************************************************************************
- void Flash (DialogPtr theDlog, short theItem) // Flashes a control's hilite on & off
- {
- Handle itemHandle;
- short itemType;
- Rect rect;
- long dummy; // This is ignored but we need somewhere to put the data returned from the sys call.
-
- GetDialogItem(theDlog, theItem, &itemType, &itemHandle, &rect); // Get item's handle, etc, etc
-
- if ((itemType==statText) || (itemType==editText))
- {
- SysBeep(1);/* Need a warning if its not the right item type. (Only control items can be hilited.)
- A FINISHED PROGRAM SHOULD NEVER SEE THIS LINE EXECUTED! */
- return;
- }
- HiliteControl((ControlRef)itemHandle, 1); // Hilite on
- Delay(10, &dummy); // hang around for 1/6 second (10 ticks)
- HiliteControl((ControlRef)itemHandle, 0); // Hilite off
- }
- //*****************************************************************************************
- // S Y S T E M 7 C H E C K
- //*****************************************************************************************
- /*
- The next bit of this program is an autonomous System 7 check. Just call Sys7Check() and it will return true if
- System 7.0 or greater is present. Oh, and don't forget its prototypes.
- */
- Boolean Sys7Check(void)
- {
- long sysVersion;
- if (!TrapAvailable(_Gestalt)) return (false);
- if (!Gestalt(gestaltSystemVersion, &sysVersion))
- {
- if (sysVersion >= 0x0700) return(true);
- }
- return(false);
- }
- //*****************************************************************************************
- Boolean TrapAvailable(short theTrap)
- {
- TrapType tType=GetTrapType(theTrap);
- if (tType==ToolTrap) {
- theTrap = (theTrap & 0x07FF);
- if (theTrap>=NumToolBoxTraps()) theTrap=_Unimplemented;
- }
- return (NGetTrapAddress(theTrap, tType) != NGetTrapAddress(_Unimplemented, ToolTrap));
- }
- //*****************************************************************************************
- TrapType GetTrapType (short theTrap)
- {
- if ((theTrap & 0x0800)>0) return(ToolTrap);
- else return(OSTrap);
- }
- //*****************************************************************************************
- short NumToolboxTraps (void)
- {
- if (NGetTrapAddress(_InitGraf, ToolTrap) == NGetTrapAddress(0xAA6E, ToolTrap)) return (0x0200);
- else return (0x0400);
- }
- //*****************************************************************************************
- //*****************************************************************************************
- // The end. To be continued...?